﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using magic8ballUtility.Sintaksa;

namespace SintaksniAnalizator
{
    public class SintaksniAnalizator
    {
        private LeksickiAnalizator.ListaUniformnihZnakova listaUniformnihZnakova;
        private LeksickiAnalizator.TablicaIzvornihZnakova tablicaIzvornihZnakova;
        private List<magic8ballUtility.Sintaksa.Produkcija> popisProdukcija;
        public List<magic8ballUtility.Sintaksa.Produkcija> PopisPrimijenjenihRedukcija { get; set; }
        private Dictionary<int, Dictionary<string, string>> tablicaAkcija;
        private Dictionary<int, Dictionary<string, string>> tablicaNovoStanje;
        private Stack<int> stog;
        private Stack<Cvor> stogCvorova;
        private List<LeksickiAnalizator.UniformniZnak> spremnikUniformnihZnakova;
        private LeksickiAnalizator.UniformniZnak ulazniSimbol;
        private int brojac;
        private HashSet<string> sinkronizacijskiZnakovi;
        public Cvor Korijen { get; set; }

        public SintaksniAnalizator(LeksickiAnalizator.ListaUniformnihZnakova listaUniformnihZnakova, LeksickiAnalizator.TablicaIzvornihZnakova tablicaIzvornihZnakova, magic8ballUtility.Sintaksa.Definicija definicija)
        {
            this.listaUniformnihZnakova = listaUniformnihZnakova;
            this.tablicaIzvornihZnakova = tablicaIzvornihZnakova;
            this.popisProdukcija = definicija.Produkcije;
            this.tablicaAkcija = definicija.TablicaAkcija;
            this.tablicaNovoStanje = definicija.TablicaNovoStanje;
            this.sinkronizacijskiZnakovi = definicija.SinkronizacijskiZnakovi;

            this.PopisPrimijenjenihRedukcija = new List<magic8ballUtility.Sintaksa.Produkcija>();
            
            stog = new Stack<int>();
            stog.Push(0);

            stogCvorova = new Stack<Cvor>();

            spremnikUniformnihZnakova = new List<LeksickiAnalizator.UniformniZnak>();

            foreach (var uniformniZnak in listaUniformnihZnakova.DohvatiUniformneZnakove())
            {
                spremnikUniformnihZnakova.Add(uniformniZnak);
            }

            spremnikUniformnihZnakova.Add(
                new LeksickiAnalizator.UniformniZnak()
                {
                    UniformniZnak_ = "§"
                });


            brojac = 0;
            ulazniSimbol = spremnikUniformnihZnakova[brojac++];

        }

        public void PokreniObradu()
        {
            while (true)
            {
                string akcija = tablicaAkcija[stog.Peek()][ulazniSimbol.UniformniZnak_];
                if (akcija == null)
                {
                    OporavakOdGreske();
                }
                else if (akcija.StartsWith("s"))
                {
                    stog.Push(Convert.ToInt32((akcija.Substring(1))));
                    stogCvorova.Push(new Cvor(ulazniSimbol.UniformniZnak_));
                    ulazniSimbol = spremnikUniformnihZnakova[brojac++];
                }
                else if (akcija.StartsWith("r"))
                {
                    var redukcija = popisProdukcija[Convert.ToInt32((akcija.Substring(1)))];
                    Cvor noviCvor = new Cvor(redukcija.Glava);
                    for (int i = 0; i < (redukcija.Tijelo.Split(' ')).Length; i++)
                    {
                        noviCvor.listaDjece.Add(stogCvorova.Pop());
                        stog.Pop();
                    }
                    stogCvorova.Push(noviCvor);
                    var novoStanje = Convert.ToInt32(tablicaNovoStanje[stog.Peek()][redukcija.Glava]);
                    stog.Push(novoStanje);
                    PopisPrimijenjenihRedukcija.Add(redukcija);
                }
                else if (akcija == "accept")
                {
                    Korijen = new Cvor(popisProdukcija.First().Glava);
                    Korijen.listaDjece.Add(stogCvorova.Pop());
                    
                    PopisPrimijenjenihRedukcija.Add(popisProdukcija[0]);
                    break;
                }
            }
        }

        private void OporavakOdGreske()
        {

            IspisPogreske();

            //trazenje sinkronizacijskog znaka, postavljanje na znak iza
            while (!sinkronizacijskiZnakovi.Contains(ulazniSimbol.UniformniZnak_))
            {
                ulazniSimbol = spremnikUniformnihZnakova[brojac++];
            }
            ulazniSimbol = spremnikUniformnihZnakova[brojac++];

            //odbacivanje neodgovarajucih stanja sa stoga
            while (tablicaAkcija[stog.Peek()][ulazniSimbol.UniformniZnak_] == null)
            {
                stogCvorova.Pop();
                stog.Pop();
            }
        }

        private void IspisPogreske()
        {
            Console.WriteLine("Pogreska u retku: {0}", ulazniSimbol.BrojReda);
            Console.WriteLine("Ocekivani znakovi:");
            foreach (var znak in tablicaAkcija[stog.Peek()].Keys)
            {
                if (tablicaAkcija[stog.Peek()][znak] != null)
                {
                    Console.WriteLine(znak);
                }
            }
            Console.WriteLine("Procitan znak: {0}, {1}",
                ulazniSimbol.UniformniZnak_,
                tablicaIzvornihZnakova.Tablica[ulazniSimbol.IndeksUTabliciZnakova].IzvorniTekst);
        }

    }
}
